# frozen_string_literal: true

require "cases/helper_sqlserver"

class SpecificSchemaTestSQLServer < ActiveRecord::TestCase
  after { SSTestEdgeSchema.delete_all }

  it "handle dollar symbols" do
    assert_difference("SSTestDollarTableName.count", 1) do
      SSTestDollarTableName.create!
    end
    assert_nothing_raised do
      SSTestDollarTableName.limit(20).offset(1)
    end
  end

  it "models can use tinyint pk tables" do
    obj = SSTestTinyintPk.create! name: "1"
    _(["Fixnum", "Integer"]).must_include obj.id.class.name
    _(SSTestTinyintPk.find(obj.id)).must_equal obj
  end

  it "be able to complex count tables with no primary key" do
    SSTestNoPkData.delete_all
    10.times { |n| SSTestNoPkData.create! name: "Test#{n}" }
    assert_equal 1, SSTestNoPkData.where(name: "Test5").count
  end

  it "quote table names properly even when they are views" do
    SSTestQuotedTable.create!
    assert_nothing_raised { assert SSTestQuotedTable.first }

    SSTestQuotedTableUser.create!
    assert_nothing_raised { assert SSTestQuotedTableUser.first }

    SSTestQuotedView1.create!
    assert_nothing_raised { assert SSTestQuotedView1.first }

    SSTestQuotedView2.create!
    assert_nothing_raised { assert SSTestQuotedView2.first }
  end

  it "cope with multi line defaults" do
    default = SSTestStringDefault.new
    assert_equal "Some long default with a\nnew line.", default.string_with_multiline_default
  end

  it "default strings before save" do
    default = SSTestStringDefault.new
    assert_nil default.string_with_null_default
    assert_equal "null", default.string_with_pretend_null_one
    assert_equal "(null)", default.string_with_pretend_null_two
    assert_equal "NULL", default.string_with_pretend_null_three
    assert_equal "(NULL)", default.string_with_pretend_null_four
    assert_equal "(3)", default.string_with_pretend_paren_three
  end

  it "default strings after save" do
    default = SSTestStringDefault.create
    assert_nil default.string_with_null_default
    assert_equal "null", default.string_with_pretend_null_one
    assert_equal "(null)", default.string_with_pretend_null_two
    assert_equal "NULL", default.string_with_pretend_null_three
    assert_equal "(NULL)", default.string_with_pretend_null_four
  end

  it "default objects work" do
    obj = SSTestObjectDefault.create! name: "MetaSkills"
    _(obj.date).must_be_nil "since this is set on insert"
    _(obj.reload.date).must_be_instance_of Date
  end

  it "allows datetime2 as timestamps" do
    _(SSTestBooking.columns_hash["created_at"].sql_type).must_equal "datetime2(7)"
    _(SSTestBooking.columns_hash["updated_at"].sql_type).must_equal "datetime2(7)"
    obj1 = SSTestBooking.new name: "test1"
    obj1.save!
    _(obj1.created_at).must_be_instance_of Time
    _(obj1.updated_at).must_be_instance_of Time
  end

  # Natural primary keys.

  it "work with identity inserts" do
    record = SSTestNaturalPkData.new name: "Test", description: "Natural identity inserts."
    record.id = "12345ABCDE"
    assert record.save
    assert_equal "12345ABCDE", record.reload.id
  end

  it "work with identity inserts when the key is an int" do
    record = SSTestNaturalPkIntData.new name: "Test", description: "Natural identity inserts."
    record.id = 12
    assert record.save
    assert_equal 12, record.reload.id
  end

  it "use primary key for row table order in pagination sql" do
    sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/
    assert_queries_match(sql) { SSTestNaturalPkData.limit(5).offset(5).load }
  end

  # Special quoted column

  it "work as normal" do
    SSTestEdgeSchema.delete_all
    r = SSTestEdgeSchema.create! "crazy]]quote" => "crazyqoute"
    assert SSTestEdgeSchema.columns_hash["crazy]]quote"]
    assert_equal r, SSTestEdgeSchema.where("crazy]]quote" => "crazyqoute").first
  end

  it "various methods to bypass national quoted columns for any column, but primarily useful for char/varchar" do
    value = Class.new do
      def quoted_id
        "'T'"
      end
    end
    # Using ActiveRecord's quoted_id feature for objects.
    assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: value.new).first }
    assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: value.new).first }
    # Using our custom char type data.
    type = ActiveRecord::Type::SQLServer::Char
    data = ActiveRecord::Type::SQLServer::Data
    assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first }
    assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first }
    # Taking care of everything.
    assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: "T").first }
    assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: "T").first }
  end

  it "can update and hence properly quoted non-national char/varchar columns" do
    o = SSTestDatatypeMigration.create!
    o.varchar_col = "O'Reilly"
    o.save!
    _(o.reload.varchar_col).must_equal "O'Reilly"
    o.varchar_col = nil
    o.save!
    _(o.reload.varchar_col).must_be_nil
  end

  # With column names that have spaces

  it "create record using a custom attribute reader and be able to load it back in" do
    value = "Saved value into a column that has a space in the name."
    record = SSTestEdgeSchema.create! with_spaces: value
    assert_equal value, SSTestEdgeSchema.find(record.id).with_spaces
  end

  # With description column

  it "allow all sorts of ordering without adapter munging it up with special description column" do
    SSTestEdgeSchema.create! description: "A"
    SSTestEdgeSchema.create! description: "B"
    SSTestEdgeSchema.create! description: "C"
    assert_equal ["A", "B", "C"], SSTestEdgeSchema.order("description").map(&:description)
    assert_equal ["A", "B", "C"], SSTestEdgeSchema.order("description asc").map(&:description)
    assert_equal ["A", "B", "C"], SSTestEdgeSchema.order("description ASC").map(&:description)
    assert_equal ["C", "B", "A"], SSTestEdgeSchema.order("description desc").map(&:description)
    assert_equal ["C", "B", "A"], SSTestEdgeSchema.order("description DESC").map(&:description)
  end

  # For uniqueidentifier model helpers

  it "returns a new id via connection newid_function" do
    acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID
    db_uuid = ActiveRecord::Base.lease_connection.newid_function
    _(db_uuid).must_match(acceptable_uuid)
  end

  # with similar table definition in two schemas

  it "returns the correct primary columns" do
    connection = ActiveRecord::Base.lease_connection
    assert_equal "field_1", connection.columns("test.sst_schema_test_multiple_schema").detect(&:is_primary?).name
    assert_equal "field_2", connection.columns("test2.sst_schema_test_multiple_schema").detect(&:is_primary?).name
  end
end
